package examples.jsf.component;

import examples.jsf.AbortProcessingException;
import examples.jsf.Application;
import examples.jsf.ConverterException;
import examples.jsf.context.FacesContext;
import examples.jsf.event.FacesEvent;
import examples.jsf.event.FacesListener;
import examples.jsf.event.PostValidateEvent;
import examples.jsf.event.PreValidateEvent;

import javax.el.ValueExpression;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;

/**
 * @author Bill
 * @version 2013-05-29
 */
public class UIComponentBase extends UIComponent {

    private UIComponent parent = null;

    @Override
    public UIComponent getParent() {
        return parent;
    }

    @Override
    public void setParent(UIComponent parent) {
        this.parent = parent;
    }

    @Override
    public boolean isRendered() {
        return false;
    }

    public void setRendered(boolean rendered) {
        //
    }

    //---------------------------------------------- Event methods

    @Override
    protected void addFacesListener(FacesListener listener) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    protected FacesListener[] getFacesListeners(Class clazz) {
        return new FacesListener[0];  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    protected void removeFacesListener(FacesListener listener) {
        //To change body of implemented methods use File | Settings | File Templates.
    }


    /**
     * @throws IllegalStateException {@inheritDoc}
     * @throws NullPointerException  {@inheritDoc}
     */
    @Override
    public void queueEvent(FacesEvent event) {

        if (event == null) {
            throw new NullPointerException();
        }
        UIComponent parent = getParent();
        if (parent == null) {
            throw new IllegalStateException();
        } else {
            parent.queueEvent(event);
        }

    }


    @Override
    public Object processSaveState(FacesContext context) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    //------------------------------------- Apply Request
    @Override
    public void processDecodes(FacesContext context) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    /**
     * 从 ViewRoot 开始，遍历组件树，调用每个组件的processValidators方法
     * 这个是一个通用的方法，可以看出，这个方法本身没有任何有效的处理，
     * 唯一所做的就是让子孙去继续处理
     *
     * UIInput中有此方法的覆盖，实现了 Input 类组件的 validation
     */
    @Override
    public void processValidators(FacesContext context) {
        if (context == null) {
            throw new NullPointerException();
        }

        // Skip processing if our rendered flag is false
        if (!isRendered()) {
            return;
        }

        pushComponentToEL(context, null);

        Application app = context.getApplication();
        app.publishEvent(context, PreValidateEvent.class, this);
        // Process all the facets and children of this component
        Iterator kids = getFacetsAndChildren();
        while (kids.hasNext()) {
            UIComponent kid = (UIComponent) kids.next();
            kid.processValidators(context);
        }
        app.publishEvent(context, PostValidateEvent.class, this);
        popComponentFromEL(context);
    }

    //------------------------------------- Update Model

    // 此方法对应 Update Model 生命周期阶段，极为重要，有众多的子类对其进行了覆盖。列举一些重要的,
    //
    @Override
    public void processUpdates(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }

        // Skip processing if our rendered flag is false
        if (!isRendered()) {
            return;
        }

        pushComponentToEL(context, null);

        // Process all facets and children of this component
        Iterator kids = getFacetsAndChildren();
        while (kids.hasNext()) {
            UIComponent kid = (UIComponent) kids.next();
            kid.processUpdates(context);

        }
        popComponentFromEL(context);

    }

    //----------------------------------------- Component Tree Management

    /*
     * <p>The <code>List</code> containing our child components.</p>
     */
    private List<UIComponent> children = null;


    public List<UIComponent> getChildren() {

        if (children == null) {
            children = new ChildrenList(this);
        }
        return (children);

    }

    @Override
    public int getChildCount() {
        if (children != null) {
            return (children.size());
        } else {
            return (0);
        }
    }

    /**
     * <p>If the specified {@link UIComponent} has a non-null parent,
     * remove it as a child or facet (as appropriate) of that parent.
     * As a result, the <code>parent</code> property will always be
     * <code>null</code> when this method returns.</p>
     *
     * @param component {@link UIComponent} to have any parent erased
     */
    private static void eraseParent(UIComponent component) {

        UIComponent parent = component.getParent();
        if (parent == null) {
            return;
        }
        if (parent.getChildCount() > 0) {
            List children = parent.getChildren();
            int index = children.indexOf(component);
            if (index >= 0) {
                children.remove(index);
                return;
            }
        }
        if (parent.getFacetCount() > 0) {
            Map facets = parent.getFacets();
            Iterator entries = facets.entrySet().iterator();
            while (entries.hasNext()) {
                Map.Entry entry = (Map.Entry) entries.next();
                //noinspection ObjectEquality
                if (entry.getValue() == component) {
                    entries.remove();
                    return;
                }
            }
        }

        // Throw an exception for the "cannot happen" case
        throw new IllegalStateException("Parent was not null, " +
                "but this component not related");

    }


    @Override
    public UIComponent findComponent(String expr) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    //----------------------------------------- Facets Management

    @Override
    public Map<String, UIComponent> getFacets() {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public UIComponent getFacet(String name) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public Iterator<UIComponent> getFacetsAndChildren() {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    //----------------------------------------- Lifecycle Management

    /**
     * 广播事件到组件或Behavior上, 调用分析：
     *  1. 主要由ViewRoot broadcaseEvents 调用
     *  2. UICommand, 还需要增加执行 ActionListener
     *
     * @throws AbortProcessingException {@inheritDoc}
     * @throws IllegalStateException    {@inheritDoc}
     * @throws NullPointerException     {@inheritDoc}
     */
    public void broadcast(FacesEvent event)
            throws AbortProcessingException {

        if (event == null) {
            throw new NullPointerException();
        }
//        if (event instanceof BehaviorEvent) {
//            BehaviorEvent behaviorEvent = (BehaviorEvent) event;
//            Behavior behavior = behaviorEvent.getBehavior();
//            behavior.broadcast(behaviorEvent);
//        }
//
//        if (listeners == null) {
//            return;
//        }
//
//        for (FacesListener listener : listeners.asArray(FacesListener.class)) {
//            if (event.isAppropriateListener(listener)) {
//                event.processListener(listener);
//            }
//        }
    }

    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public void decode(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }
//        String rendererType = getRendererType();
//        if (rendererType != null) {
//            Renderer renderer = this.getRenderer(context);
//            if (renderer != null) {
//                renderer.decode(context, this);
//            } else {
//                if (LOGGER.isLoggable(Level.FINE)) {
//                    LOGGER.fine("Can't get Renderer for type " + rendererType);
//                }
//            }
//        }
    }


    public void encodeBegin(FacesContext context) throws IOException {}
    public void encodeChildren(FacesContext context) throws IOException {

        if (context == null) {
            throw new NullPointerException();
        }
        if (!isRendered()) {
            return;
        }
//        String rendererType = getRendererType();
//        if (rendererType != null) {
//            Renderer renderer = this.getRenderer(context);
//            if (renderer != null) {
//                renderer.encodeChildren(context, this);
//            }
//            // We've already logged for this component
//        } else {
//            if (getChildCount() > 0) {
//                for (UIComponent child : getChildren()) {
//                    child.encodeAll(context);
//                }
//            }
//        }
    }
    public void encodeEnd(FacesContext context) throws IOException {}


    // Private implementation of List that supports the functionality
    // required by UIComponent.getChildren()
    private static class ChildrenList extends ArrayList<UIComponent> {

        private UIComponent component;

        public ChildrenList(UIComponent component) {
            super(6);
            this.component = component;
        }

        public void add(int index, UIComponent element) {
            if (element == null) {
                throw new NullPointerException();
            } else if ((index < 0) || (index > size())) {
                throw new IndexOutOfBoundsException();
            } else {
                eraseParent(element);
                super.add(index, element);
                element.setParent(component);

            }
        }

        public boolean add(UIComponent element) {
            if (element == null) {
                throw new NullPointerException();
            } else {
                eraseParent(element);
                boolean result = super.add(element);
                element.setParent(component);
                return result;
            }
        }

        public boolean addAll(Collection<? extends UIComponent> collection) {
            Iterator<UIComponent> elements =
                    (new ArrayList<UIComponent>(collection)).iterator();
            boolean changed = false;
            while (elements.hasNext()) {
                UIComponent element = elements.next();
                if (element == null) {
                    throw new NullPointerException();
                } else {
                    add(element);
                    changed = true;
                }
            }
            return (changed);
        }

        public boolean addAll(int index, Collection<? extends UIComponent> collection) {
            Iterator<UIComponent> elements =
                    (new ArrayList<UIComponent>(collection)).iterator();
            boolean changed = false;
            while (elements.hasNext()) {
                UIComponent element = elements.next();
                if (element == null) {
                    throw new NullPointerException();
                } else {
                    add(index++, element);
                    changed = true;
                }
            }
            return (changed);
        }

        public void clear() {
            int n = size();
            if (n < 1) {
                return;
            }
            for (int i = 0; i < n; i++) {
                UIComponent child = get(i);
                child.setParent(null);
            }
            super.clear();
        }

        public Iterator<UIComponent> iterator() {
            return (new ChildrenListIterator(this));
        }

        public ListIterator<UIComponent> listIterator() {
            return (new ChildrenListIterator(this));
        }

        public ListIterator<UIComponent> listIterator(int index) {
            return (new ChildrenListIterator(this, index));
        }

        public UIComponent remove(int index) {
            UIComponent child = get(index);
            super.remove(index);
            child.setParent(null);
            return (child);
        }

        public boolean remove(Object elementObj) {
            UIComponent element = (UIComponent) elementObj;
            if (element == null) {
                throw new NullPointerException();
            }

            if (super.remove(element)) {
                element.setParent(null);
                return (true);
            } else {
                return (false);
            }
        }

        public boolean removeAll(Collection<?> collection) {
            boolean result = false;
            for (Object elements : collection) {
                if (remove(elements)) {
                    result = true;
                }
            }
            return (result);
        }

        public boolean retainAll(Collection<?> collection) {
            boolean modified = false;
            Iterator<?> items = iterator();
            while (items.hasNext()) {
                if (!collection.contains(items.next())) {
                    items.remove();
                    modified = true;
                }
            }
            return (modified);
        }

        public UIComponent set(int index, UIComponent element) {
            if (element == null) {
                throw new NullPointerException();
            } else if ((index < 0) || (index >= size())) {
                throw new IndexOutOfBoundsException();
            } else {
                eraseParent(element);
                UIComponent previous = get(index);
                super.set(index, element);
                previous.setParent(null);
                element.setParent(component);
                return (previous);
            }
        }
    }

    // Private implementation of ListIterator for ChildrenList
    private static class ChildrenListIterator implements ListIterator<UIComponent> {


        public ChildrenListIterator(ChildrenList list) {
            this.list = list;
            this.index = 0;
        }

        public ChildrenListIterator(ChildrenList list, int index) {
            this.list = list;
            if ((index < 0) || (index > list.size())) {
                throw new IndexOutOfBoundsException(String.valueOf(index));
            } else {
                this.index = index;
            }
        }


        private ChildrenList list;
        private int index;
        private int last = -1; // Index last returned by next() or previous()

        // Iterator methods

        public boolean hasNext() {
            return (index < list.size());
        }

        public UIComponent next() {
            try {
                UIComponent o = list.get(index);
                last = index++;
                return (o);
            } catch (IndexOutOfBoundsException e) {
                throw new NoSuchElementException(String.valueOf(index));
            }
        }

        public void remove() {
            if (last == -1) {
                throw new IllegalStateException();
            }
            list.remove(last);
            if (last < index) {
                index--;
            }
            last = -1;
        }

        // ListIterator methods

        public void add(UIComponent o) {
            last = -1;
            list.add(index++, o);
        }

        public boolean hasPrevious() {
            return (index > 1);
        }

        public int nextIndex() {
            return (index);
        }

        public UIComponent previous() {
            try {
                int current = index - 1;
                UIComponent o = list.get(current);
                last = current;
                index = current;
                return (o);
            } catch (IndexOutOfBoundsException e) {
                throw new NoSuchElementException();
            }
        }

        public int previousIndex() {
            return (index - 1);
        }

        public void set(UIComponent o) {
            if (last == -1) {
                throw new IllegalStateException();
            }
            list.set(last, o);
        }

    }


    //----------------------------------------- State Management

    @Override
    public Object saveState(FacesContext context) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void restoreState(FacesContext context, Object state) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public boolean isTransient() {
        return false;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void setTransient(boolean newTransientValue) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

}
